<template>
  <!-- vue-seamless-scroll 滚动组件源码 基于VUE2.0 -->
  <!--
    * 请在组件最外层样式中使用 position: relative
    * 否则scroll宽度为屏幕宽度
   -->
  <div class="scroll" :style="{ height: `${innerHeight}px` }">
    <div ref="wrap" class="wrap" :style="{ height: `${innerHeight}px` }">
      <div ref="realBox" :style="pos" @mouseenter="enter" @mouseleave="leave" @touchstart="touchStart"
        @touchmove="touchMove" @touchend="touchEnd" class="real_box">
        <div ref="slotList" :style="float">
          <slot></slot>
        </div>
        <div v-html="copyHtml" :style="float" ref="copyList"></div>
      </div>
    </div>
    <div class="btns" v-if="showBtn">
      <div class="up_btn" @click="upClick" @mouseenter="enter" @mouseleave="leave" @touchstart="touchStart"
        @touchmove="touchMove" @touchend="touchEnd">
        <!-- 向上滚动一个单位  -->
      </div>
      <div class="down_btn" @click="downClick" @mouseenter="enter" @mouseleave="leave" @touchstart="touchStart"
        @touchmove="touchMove" @touchend="touchEnd">
        <!-- 向下滚动一个单位  -->
      </div>
    </div>
  </div>
</template>

<script>
import { copyObj, arrayEqual } from './scrollList.js';
export default {
  name: 'VueSeamlessScroll',
  props: {
    data: {
      type: Array,
      default: () => {
        return [];
      },
    },
    classOption: {
      type: Object,
      default: () => {
        return {};
      },
    },
    innerHeight: {
      type: Number,
      default: () => 250,
    },
    showBtn: {
      type: Boolean,
      default: () => true,
    },
    backgroundShow: {
      type: Boolean,
      default: () => true,
    },
  },
  data() {
    return {
      xPos: 0,
      yPos: 0,
      delay: 0,
      copyHtml: '',
      height: 0,
      width: 0, // 外容器宽度
      realBoxWidth: 0, // 内容实际宽度
    };
  },
  computed: {
    leftSwitchState() {
      return this.xPos < 0;
    },
    rightSwitchState() {
      return Math.abs(this.xPos) < this.realBoxWidth - this.width;
    },
    leftSwitchClass() {
      return this.leftSwitchState ? '' : this.options.switchDisabledClass;
    },
    rightSwitchClass() {
      return this.rightSwitchState ? '' : this.options.switchDisabledClass;
    },
    leftSwitch() {
      return {
        position: 'absolute',
        margin: `${this.height / 2}px 0 0 -${this.options.switchOffset}px`,
        transform: 'translate(-100%,-50%)',
      };
    },
    rightSwitch() {
      return {
        position: 'absolute',
        margin: `${this.height / 2}px 0 0 ${this.width + this.options.switchOffset}px`,
        transform: 'translateY(-50%)',
      };
    },
    float() {
      return this.isHorizontal ? { float: 'left', overflow: 'hidden' } : { overflow: 'hidden' };
    },
    pos() {
      return {
        transform: `translate(${this.xPos}px,${this.yPos}px)`,
        transition: `all ${this.ease} ${this.delay}ms`,
        overflow: 'hidden',
      };
    },
    defaultOption() {
      return {
        step: 1, //步长
        limitMoveNum: 9, //启动无缝滚动最小数据数
        hoverStop: true, //是否启用鼠标hover控制
        direction: 1, // 0 往下 1 往上 2向左 3向右
        openTouch: true, //开启移动端touch
        singleHeight: 0, //单条数据高度有值hoverStop关闭
        singleWidth: 0, //单条数据宽度有值hoverStop关闭
        waitTime: 800, //单步停止等待时间
        switchOffset: 30,
        autoPlay: true,
        navigation: false,
        switchSingleStep: 134,
        switchDelay: 400,
        switchDisabledClass: 'disabled',
        isSingleRemUnit: false, // singleWidth/singleHeight 是否开启rem度量
      };
    },
    options() {
      return copyObj({}, this.defaultOption, this.classOption);
    },
    navigation() {
      return this.options.navigation;
    },
    autoPlay() {
      if (this.navigation) return false;
      return this.options.autoPlay;
    },
    scrollSwitch() {
      return this.data.length >= this.options.limitMoveNum;
    },
    hoverStopSwitch() {
      return this.options.hoverStop && this.autoPlay && this.scrollSwitch;
    },
    canTouchScroll() {
      return this.options.openTouch;
    },
    isHorizontal() {
      return this.options.direction > 1;
    },
    baseFontSize() {
      return this.options.isSingleRemUnit
        ? parseInt(window.getComputedStyle(document.documentElement, null).fontSize)
        : 1;
    },
    realSingleStopWidth() {
      return this.options.singleWidth * this.baseFontSize;
    },
    realSingleStopHeight() {
      return this.options.singleHeight * this.baseFontSize;
    },
    step() {
      let singleStep;
      let step = this.options.step;
      if (this.isHorizontal) {
        singleStep = this.realSingleStopWidth;
      } else {
        singleStep = this.realSingleStopHeight;
      }
      if (singleStep > 0 && singleStep % step > 0) {
        console.error(
          '如果设置了单步滚动,step需是单步大小的约数,否则无法保证单步滚动结束的位置是否准确。~~~~~'
        );
      }
      return step;
    },
  },
  watch: {
    data(newData, oldData) {
      this._dataWarm(newData);
      //监听data是否有变更
      if (!arrayEqual(newData, oldData)) {
        this.reset();
      }
    },
    autoPlay(bol) {
      if (bol) {
        this.reset();
      } else {
        this._stopMove();
      }
    },
  },
  mounted() {
    this._initMove();
  },
  beforeCreate() {
    this.reqFrame = null; // move动画的animationFrame定时器
    this.singleWaitTime = null; // single 单步滚动的定时器
    this.isHover = false; // mouseenter mouseleave 控制this._move()的开关
    this.ease = 'ease-in';
  },
  beforeUnmount() {
    this._cancle();
    clearTimeout(this.singleWaitTime);
  },
  methods: {
    reset() {
      this._cancle();
      this._initMove();
    },
    // 向上翻单个数据
    upClick() {
      const h = this.realBoxHeight / 2;
      if (Math.abs(this.yPos) < 40) {
        this.yPos = -h + this.options.singleHeight;
        return;
      }
      this.yPos += this.options.singleHeight;
      this.startPosY = this.yPos;
    },
    // 向下翻单个数据
    downClick() {
      // console.log(this.realBoxHeight / 2)
      const h = this.realBoxHeight / 2;
      const last = Math.abs(this.yPos);
      if (last >= h) {
        this.yPos = -this.options.singleHeight;
        // console.log(this.yPos)
      } else {
        this.yPos -= this.options.singleHeight;
        // console.log(this.yPos)
      }
      this.startPosY = this.yPos;
    },
    _cancle() {
      cancelAnimationFrame(this.reqFrame || '');
    },
    touchStart(e) {
      if (!this.canTouchScroll) return;
      let timer;
      const touch = e.targetTouches[0]; //touches数组对象获得屏幕上所有的touch，取第一个touch
      const { waitTime, singleHeight, singleWidth } = this.options;
      this.startPos = {
        x: touch.pageX,
        y: touch.pageY,
      }; //取第一个touch的坐标值
      this.startPosY = this.yPos; //记录touchStart时候的posY
      this.startPosX = this.xPos; //记录touchStart时候的posX
      if (!!singleHeight && !!singleWidth) {
        if (timer) clearTimeout(timer);
        timer = setTimeout(() => {
          this._cancle();
        }, waitTime + 20);
      } else {
        this._cancle();
      }
    },
    touchMove(e) {
      //当屏幕有多个touch或者页面被缩放过，就不执行move操作
      if (!this.canTouchScroll || e.targetTouches.length > 1 || (e.scale && e.scale !== 1))
        return;
      const touch = e.targetTouches[0];
      const { direction } = this.options;
      this.endPos = {
        x: touch.pageX - this.startPos.x,
        y: touch.pageY - this.startPos.y,
      };
      event.preventDefault(); //阻止触摸事件的默认行为，即阻止滚屏
      const dir = Math.abs(this.endPos.x) < Math.abs(this.endPos.y) ? 1 : 0; //dir，1表示纵向滑动，0为横向滑动
      if (dir === 1 && direction < 2) {
        // 表示纵向滑动 && 运动方向为上下
        this.yPos = this.startPosY + this.endPos.y;
      } else if (dir === 0 && direction > 1) {
        // 为横向滑动 && 运动方向为左右
        this.xPos = this.startPosX + this.endPos.x;
      }
    },
    touchEnd() {
      if (!this.canTouchScroll) return;
      let timer;
      const direction = this.options.direction;
      this.delay = 50;
      if (direction === 1) {
        if (this.yPos > 0) this.yPos = 0;
      } else if (direction === 0) {
        let h = (this.realBoxHeight / 2) * -1;
        if (this.yPos < h) this.yPos = h;
      } else if (direction === 2) {
        if (this.xPos > 0) this.xPos = 0;
      } else if (direction === 3) {
        let w = this.realBoxWidth * -1;
        if (this.xPos < w) this.xPos = w;
      }
      if (timer) clearTimeout(timer);
      timer = setTimeout(() => {
        this.delay = 0;
        this._move();
      }, this.delay);
    },
    enter() {
      if (this.hoverStopSwitch) this._stopMove();
    },
    leave() {
      if (this.hoverStopSwitch) this._startMove();
    },
    _move() {
      // 鼠标移入时拦截_move()
      if (this.isHover) return;
      this._cancle(); //进入move立即先清除动画 防止频繁touchMove导致多动画同时进行
      this.reqFrame = requestAnimationFrame(
        function () {
          const h = this.realBoxHeight / 2; //实际高度
          const w = this.realBoxWidth / 2; //宽度
          let { direction, waitTime } = this.options;
          let { step } = this;
          if (direction === 1) {
            // 上
            if (Math.abs(this.yPos) >= h) {
              this.$emit('ScrollEnd');
              this.yPos = 0;
            }
            this.yPos -= step;
          } else if (direction === 0) {
            // 下
            if (this.yPos >= 0) {
              this.$emit('ScrollEnd');
              this.yPos = h * -1;
            }
            this.yPos += step;
          } else if (direction === 2) {
            // 左
            if (Math.abs(this.xPos) >= w) {
              this.$emit('ScrollEnd');
              this.xPos = 0;
            }
            this.xPos -= step;
          } else if (direction === 3) {
            // 右
            if (this.xPos >= 0) {
              this.$emit('ScrollEnd');
              this.xPos = w * -1;
            }
            this.xPos += step;
          }
          if (this.singleWaitTime) clearTimeout(this.singleWaitTime);
          if (!!this.realSingleStopHeight) {
            //是否启动了单行暂停配置
            if (Math.abs(this.yPos) % this.realSingleStopHeight < step) {
              // 符合条件暂停waitTime
              this.singleWaitTime = setTimeout(() => {
                this._move();
              }, waitTime);
            } else {
              this._move();
            }
          } else if (!!this.realSingleStopWidth) {
            if (Math.abs(this.xPos) % this.realSingleStopWidth < step) {
              // 符合条件暂停waitTime
              this.singleWaitTime = setTimeout(() => {
                this._move();
              }, waitTime);
            } else {
              this._move();
            }
          } else {
            this._move();
          }
        }.bind(this)
      );
    },
    _initMove() {
      console.log(
        '当前条目数',
        this.data.length,
        '条目数量大于',
        this.options.limitMoveNum,
        '则可以滚动'
      );
      this.$nextTick(() => {
        const { switchDelay } = this.options;
        const { autoPlay, isHorizontal } = this;
        this._dataWarm(this.data);
        this.copyHtml = ''; //清空copy
        if (isHorizontal) {
          this.height = this.$refs.wrap.offsetHeight;
          this.width = this.$refs.wrap.offsetWidth;
          let slotListWidth = this.$refs.slotList.offsetWidth;
          // 水平滚动设置warp width
          if (autoPlay) {
            // 修正offsetWidth四舍五入
            slotListWidth = slotListWidth * 2 + 1;
          }
          this.$refs.realBox.style.width = slotListWidth + 'px';
          this.realBoxWidth = slotListWidth;
        }

        if (autoPlay) {
          this.ease = 'ease-in';
          this.delay = 0;
        } else {
          this.ease = 'linear';
          this.delay = switchDelay;
          return;
        }

        // 是否可以滚动判断
        if (this.scrollSwitch) {
          this._startMove();
          let timer;
          if (timer) clearTimeout(timer);
          this.copyHtml = this.$refs.slotList.innerHTML;

          setTimeout(() => {
            if (this.data.length % 2 !== 0 && this.backgroundShow) {
              console.log('条目为奇数 复制的html里面的条纹反着来');
              const list = this.$refs.copyList.getElementsByTagName('li');
              for (let i = 0; i <= list.length - 1; i++) {
                if (i % 2 !== 0) list[i].style.background = 'rgb(72, 173, 255, 0.1)';
                else list[i].style.background = 'transparent';
              }
            }

            this.realBoxHeight = this.$refs.realBox.offsetHeight;
            this._move();
          }, 0);
        } else {
          this._cancle();
          this._stopMove();
          this.yPos = this.xPos = 0;
        }
      });
    },
    _dataWarm(data) {
      if (data.length > 100) {
        console.warn(`数据达到了${data.length}条有点多哦~,可能会造成部分老旧浏览器卡顿。`);
      }
    },
    _startMove() {
      console.log('开始滚动');
      this.isHover = false; //开启_move
      this._move();
    },
    _stopMove() {
      console.log('停止滚动');
      this.isHover = true; //关闭_move
      // 防止频频hover进出单步滚动,导致定时器乱掉
      if (this.singleWaitTime) clearTimeout(this.singleWaitTime);
      this._cancle();
    },
  },
};
</script>
<style lang="scss" scoped>
.scroll {
  position: absolute;
  width: 100%;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;

  .wrap {
    position: relative;
    overflow: hidden;
    width: 100%;
  }

  .btns {
    display: flex;
    margin-top: 16px;

    .up_btn {
      width: 24px;
      height: 24px;
      margin-right: 6px;
      background: url('./imgs/up.png') no-repeat;
      background-size: 100% 100%;
      cursor: pointer;

      &:hover {
        background: url('./imgs/up_active.png') no-repeat;
        background-size: 100% 100%;
      }
    }

    .down_btn {
      width: 24px;
      height: 24px;
      background: url('./imgs/down.png') no-repeat;
      background-size: 100% 100%;
      cursor: pointer;

      &:hover {
        background: url('./imgs/down_active.png') no-repeat;
        background-size: 100% 100%;
      }
    }
  }
}
</style>
